﻿#include "EditorVariableAbilityViewModel.h"

#include "Asset/EditorDefaultPackage.h"

#include "Framework/Editor.h"
#include "Language/EditorScriptDebugSubsystem.h"

void REditorVariableAbilityViewModel::BindModel(TSharedObjectPtr<REditorAbilityModel> InModel)
{
    Super::BindModel(InModel);
    VariableModel = InModel;

    Icon = REditorDefaultPackage::Get()->GetIcon(U"CodiconDebugAlt");
    HighlightIcon = REditorDefaultPackage::Get()->GetIcon(U"CodiconDebugAlt_Highlight");

    ExpendedIcon= REditorDefaultPackage::Get()->GetIcon(U"CodiconChevronDown");
    CollapsedIcon= REditorDefaultPackage::Get()->GetIcon(U"CodiconChevronRight");

    REditor::Get()->GetSubsystem<REditorScriptDebugSubsystem>()
        ->OnReferenceChildrenReceived.BindLambda([this](
            const SEditorScriptValueReference &InReference
            , const TVector<SEditorScriptValue> &InValues)
    {
        OnChildrenRequested(InReference,InValues);
    });

    REditor::Get()->GetSubsystem<REditorScriptDebugSubsystem>()
        ->OnSelectedFrameDataChanged.BindLambda([this]()
    {
        ExpandedScopes.Add(U"局部变量");
        RequestingValues.Clear();
        RequestedValuesMap.Clear();
        UpdateNodeList();
    });
    UpdateNodeList();

    REditor::Get()->GetSubsystem<REditorScriptDebugSubsystem>()
        ->OnDebugStopped.BindLambda([this]()
    {
        ExpandedScopes.Clear();
        ExpandedValues.Clear();
        RequestingValues.Clear();
        RequestedValuesMap.Clear();
        UpdateNodeList();
    });
}

TSharedObjectPtr<RTexture2D> REditorVariableAbilityViewModel::GetIcon() const
{
    return Icon;
}

TSharedObjectPtr<RTexture2D> REditorVariableAbilityViewModel::GetHighlightIcon() const
{
    return HighlightIcon;
}

void REditorVariableAbilityViewModel::UpdateNodeList()
{
    NodeList.Clear();

    auto FrameDataManager=REditor::Get()->GetSubsystem<REditorScriptDebugSubsystem>();

    if(FrameDataManager->GetSelectedFrameDataParameterNum()
        +FrameDataManager->GetSelectedFrameDataLocalVariableNum()==0)
    {
        OnListChanged.ExecuteIfBound();
        return;
    }

    auto LocalNode=std::make_shared<SVariableNode>();
    LocalNode->IsScope=true;
    LocalNode->Name=U"局部变量";
    LocalNode->bHasChildren=true;
    LocalNode->Depth=0;
    LocalNode->bIsExpanded=ExpandedScopes.Contains(LocalNode->Name);

    NodeList.Add(LocalNode);

    if(LocalNode->bIsExpanded)
    {
        for(int64_t i=0;i<FrameDataManager->GetSelectedFrameDataParameterNum();i++)
        {
            auto Node=std::make_shared<SVariableNode>();
            Node->Name=FrameDataManager->GetSelectedFrameDataParameterName(i);
            Node->Reference=FrameDataManager->GetSelectedFrameDataParameterReference(i);
            Node->Value=FrameDataManager->GetSelectedFrameDataParameterValue(i);
            Node->Depth=1;
            Node->bHasChildren=FrameDataManager->HasSelectedFrameDataParameterChildren(i);
            if(Node->bHasChildren)
            {
                Node->bIsExpanded=ExpandedValues.Contains(Node->Reference);
            }
            else
            {
                Node->bIsExpanded=false;
            }

            NodeList.Add(Node);
        
            if(Node->bIsExpanded)
            {
                UpdateNodeChildren(Node->Reference,Node->Depth);
            }
        }


        for(int64_t i=0;i<FrameDataManager->GetSelectedFrameDataLocalVariableNum();i++)
        {
            auto Node=std::make_shared<SVariableNode>();
            Node->Name=FrameDataManager->GetSelectedFrameDataLocalVariableName(i);
            Node->Reference=FrameDataManager->GetSelectedFrameDataLocalVariableReference(i);
            Node->Value=FrameDataManager->GetSelectedFrameDataLocalVariableValue(i);
            Node->Depth=1;
            Node->bHasChildren=FrameDataManager->HasSelectedFrameDataLocalVariableChildren(i);
            if(Node->bHasChildren)
            {
                Node->bIsExpanded=ExpandedValues.Contains(Node->Reference);
            }
            else
            {
                Node->bIsExpanded=false;
            }
            NodeList.Add(Node);

            if(Node->bIsExpanded)
            {
                UpdateNodeChildren(Node->Reference,Node->Depth);
            }
        }
    }

    OnListChanged.ExecuteIfBound();
}

void REditorVariableAbilityViewModel::UpdateNodeChildren(
    const SEditorScriptValueReference &InReference
    , int64_t InParentDepth)
{
    auto ChildrenValuesPtr=RequestedValuesMap.Find(InReference);
    if(!ChildrenValuesPtr)
    {
        //the children has not been requested
        return;
    }

    for(auto& Value:*ChildrenValuesPtr)
    {
        auto ChildNode=std::make_shared<SVariableNode>();
        ChildNode->Name=Value.Name;
        ChildNode->Reference=Value.Reference;
        ChildNode->Value=Value.Value;
        ChildNode->Depth=InParentDepth+1;
        ChildNode->bHasChildren=Value.HasChildren();
        if(ChildNode->bHasChildren)
        {
            ChildNode->bIsExpanded=ExpandedValues.Contains(ChildNode->Reference);
        }
        else
        {
            ChildNode->bIsExpanded=false;
        }

        NodeList.Add(ChildNode);

        if(ChildNode->bIsExpanded)
        {
            UpdateNodeChildren(ChildNode->Reference,ChildNode->Depth);
        }
    }

}

void REditorVariableAbilityViewModel::OnChildrenRequested(const SEditorScriptValueReference &InReference, const TVector<SEditorScriptValue> &InValues)
{
    auto FrameDataManager=REditor::Get()->GetSubsystem<REditorScriptDebugSubsystem>();

    if(!RequestingValues.Contains(InReference))
    {
        return;
    }
    
    RequestingValues.Remove(InReference);
    RequestedValuesMap.Add(InReference,InValues);

    UpdateNodeList();

}

TSharedObjectPtr<RTexture2D> REditorVariableAbilityViewModel::GetNodeIcon(int64_t InIndex) const
{
    if(NodeList[InIndex]->bHasChildren)
    {
        return NodeList[InIndex]->bIsExpanded?ExpendedIcon:CollapsedIcon;
    }
    return nullptr;
}

int64_t REditorVariableAbilityViewModel::GetNodeNum() const
{
    return NodeList.Num();
}

bool REditorVariableAbilityViewModel::IsNodeScope(int64_t InIndex) const
{
    return NodeList[InIndex]->IsScope;
}

bool REditorVariableAbilityViewModel::IsNodeExpanded(int64_t InIndex) const
{
    return NodeList[InIndex]->bIsExpanded;
}

void REditorVariableAbilityViewModel::ToggleNodeExpanded(int64_t InIndex)
{
    NodeList[InIndex]->bIsExpanded=!NodeList[InIndex]->bIsExpanded;

    if(NodeList[InIndex]->IsScope)
    {
        if(NodeList[InIndex]->bIsExpanded)
        {
            ExpandedScopes.Add(NodeList[InIndex]->Name);
        }
        else
        {
            ExpandedScopes.Remove(NodeList[InIndex]->Name);
        }
        UpdateNodeList();
        return;
    }

    //try to request the children
    if(!NodeList[InIndex]->bIsExpanded)
    {
        ExpandedValues.Remove(NodeList[InIndex]->Reference);
        RequestingValues.Remove(NodeList[InIndex]->Reference);
        UpdateNodeList();
        return ;
    }

    RequestingValues.Add(NodeList[InIndex]->Reference);
    ExpandedValues.Add(NodeList[InIndex]->Reference);
    UpdateNodeList();

    auto FrameDataManager=REditor::Get()->GetSubsystem<REditorScriptDebugSubsystem>();
    FrameDataManager->RequestReferenceChildren(NodeList[InIndex]->Reference);

}

CString REditorVariableAbilityViewModel::GetNodeName(int64_t InIndex) const
{
    return NodeList[InIndex]->Name;
}

CString REditorVariableAbilityViewModel::GetNodeValue(int64_t InIndex) const
{
    return NodeList[InIndex]->Value;
}

bool REditorVariableAbilityViewModel::HasNodeChildren(int64_t InIndex) const
{
    return NodeList[InIndex]->bHasChildren;
}

float REditorVariableAbilityViewModel::GetNodeIndent(int64_t InIndex) const
{
    return NodeList[InIndex]->Depth * IndentSize;
}
